home *** CD-ROM | disk | FTP | other *** search
/ TPUG - Toronto PET Users Group / TPUG Users Group CD / TPUG Users Group CD.iso / AMIGA / AMICUS / AMICUS15.ADF / Texts / Spawn.DOC < prev    next >
Text File  |  1988-04-20  |  16KB  |  479 lines

  1.  One of the questions that is often asked about the Amiga is:
  2.  
  3.          HOW DO I USE THE MULTI-TASKING CAPABILITIES?
  4.  
  5.  There are actually two different levels of multi-tasking that are
  6.  possible on the Amiga, and which of these that you use depends on
  7.  the nature of the subtask you wish to perform.
  8.  
  9.  o       If your code is largely self-contained, and does no I/O and
  10.          uses no disk-resident library code, you can probably spawn
  11.          a task, as is demonstrated in the task-spawning message.
  12.  
  13.  o       If your code needs to do I/O, (or uses AmigaDOS functions
  14.          in ANY way), it will have to be spawned as a process rather
  15.          than as a task.  MULTI-PROCESSING is shown here.
  16.  
  17.  
  18.  WHAT IS COVERED HERE?
  19.  
  20.  There are two programs contained in this message:
  21.  
  22.  o littleproc.c - a demonstration routine that is spawned by
  23.   another process.  The normal start-up code with which it
  24.   is linked automatically waits for a workbench start-up message
  25.   before it gets going.  Using the same message port that was
  26.   provided when the process was initiated, it again goes to
  27.   sleep waiting for a message that contains specific information...
  28.   in this case, the parameters that the master program is using,
  29.   namely its stdout and stderr file handles.  Thus, this spawned
  30.   process can be made to output to the same window from which
  31.   the originating process was begun.
  32.  
  33.  o proctest.c - a demo program that loads and starts littleproc,
  34.   and unloads its code and data when it finishes.
  35.  
  36.  A process is a superset of a task, and the various AmigaDOS routines
  37.  require that a process control block and its associated information
  38.  be available in order to run.  Thus this code is provided to allow
  39.  a user who requires a process rather than a task to have an example
  40.  on which to build.
  41.  
  42.  The example was tested from the CLI, and compiled under Amiga C, 
  43.  (Lattice 3.03).  
  44.  
  45.  Link information:
  46.  ; "process.with"
  47.  
  48.  FROM lib:Astartup.obj process.o
  49.  TO process
  50.  LIBRARY lib:amiga.lib
  51.  
  52.  ; "littleproc.with"
  53.  
  54.  FROM lib:Astartup.obj littleproc.o
  55.  TO littleproc
  56.  LIBRARY lib:amiga.lib
  57.  
  58.  DISCLAIMER:
  59.  
  60.          This program is provided as a service to the programmer
  61.          community to demonstrate one or more features of the Amiga
  62.          personal computer.  These code samples may be freely used
  63.          for commercial or noncommercial purposes.
  64.  
  65.          Commodore Electronics, Ltd ("Commodore") makes no
  66.          warranties, either expressed or implied, with respect
  67.          to the program described herein, its quality, performance,
  68.          merchantability, or fitness for any particular purpose.
  69.          This program is provided "as is" and the entire risk
  70.          as to its quality and performance is with the user.
  71.          Should the program prove defective following its
  72.          purchase, the user (and not the creator of the program,
  73.          Commodore, their distributors or their retailers)
  74.          assumes the entire cost of all consequent damages.  In 
  75.          no event will Commodore be liable for direct, indirect,
  76.          incidental or consequential damages resulting from any
  77.          defect in the program even if it has been advised of the 
  78.          possibility of such damages.  Some laws do not allow
  79.          the exclusion or limitation of implied warranties or
  80.          liabilities for incidental or consequential damages,
  81.          so the above limitation or exclusion may not apply.
  82.  
  83.  
  84.  Program dependencies:
  85.  
  86.    Use c-devel:examples/makesimple on proctest.c and
  87.   littleproc.c BUT modify makesimple to use:
  88.  
  89.    Astartup.obj and amiga.lib only instead of
  90.    Lstartup.obj and lc.lib+amiga.lib.  
  91.  
  92.   AND specify "-v" option (delete stack checking)
  93.   for lc2.  This eliminates any need to link with
  94.   lc.lib or Lstartup.obj for this particular example.
  95.  
  96.   Note:  I haven't tried to make this compatible with
  97.   the Lattice start-up code.... the only point of
  98.   incompatibility is my own use of stdout/stderr.
  99.   Lattirovide "printf" at link time,
  100.   printf internally uses the AmigaDOS version of stdout.
  101.   If you link with lc.lib specified first, then it 
  102.   uses its own interpretation of stdout, so they
  103.   will not be compatible with the values received from
  104.   the Open() function ("O"pen() is AmigaDOS, "o"pen is Lattice).
  105.  
  106.   Lattice defines stdout/stderr as the address of an i/o block.
  107.   If the main process is compiled under Lattice and
  108.   the slave process is also compiled under Lattice,
  109.   then the values passed for stdout and stderr
  110.   will be compatible.  If not, then there is probably
  111.   something a person could do to make them compatible.
  112.  
  113.   Perhaps some other person would care to investigate
  114.   this and provide a translation method between the
  115.   two.  (Its a problem when one tries to mix two
  116.   different manufacturer's idea of underlying code
  117.   support.)
  118.  
  119.   
  120.  The purpose of the program is to allow you to see how a slave process
  121.  can be initialized and started.  Please ignore the stdout/stderr
  122.  difficulties, and utilize whatever methods you wish for I/O.
  123.  
  124.  
  125.  
  126.  
  127.  /* proctest.c *************************************************** */
  128.  
  129.  /* author:  Rob Peck   3/14/86                 */
  130.  /* system software version: V1.1               */
  131.  
  132.  #include "exec/types.h"
  133.  #include "exec/nodes.h"
  134.  #include "exec/lists.h"
  135.  #include "exec/libraries.h"
  136.  #include "exec/ports.h"
  137.  #include "exec/interrupts.h"
  138.  #include "exec/io.h"
  139.  #include "exec/memory.h"
  140.  #include "libraries/dos.h"
  141.  #include "libraries/dosextens.h"
  142.  
  143.  #include "workbench/startup.h"
  144.  
  145.  #define PRIORITY 0
  146.  #define STACKSIZE 5000
  147.  
  148.  extern struct Message *GetMsg();
  149.  extern int LoadSeg();
  150.  extern struct MsgPort *CreateProc();
  151.  
  152.  struct MyMess { 
  153.     struct Message mm_Message;
  154.     int     mm_OutPointer;
  155.     int     mm_ErrPointer; 
  156.  };
  157.  
  158.  extern int stdout;
  159.  extern int stderr; 
  160.  
  161.  main()
  162.  {
  163.   struct Message *reply;
  164.   struct Process *myprocess;
  165.  
  166.   /* Message that we send to the process to wake it up */
  167.  
  168.   struct WBStartup *msg;  
  169.   
  170.   /* Message to contain my own parameters to pass on to spawned 
  171.    * process, sample only.  Just to prove that we correctly
  172.    * create a process, we are giving it something other than nil: 
  173.    * as its stdout and stderr... in fact, giving it OUR values 
  174.    * so we can share the same output window.  
  175.    */
  176.   struct MyMess *parms; 
  177.  
  178.   /* Because main() is itself started as a process, it automatically
  179.    * has a message port allocated for itself.  Located at
  180.    *  &((struct Process *)FindTask(0))->pr_MsgPort 
  181.    */
  182.  
  183.   int littleSeg; 
  184.  
  185.   /* Actually littleSeg is a BPTR, but the int declaration
  186.    * keeps the compiler happy and we don't use the
  187.    * value ourselves anyhow... just pass it on.
  188.    */
  189.  
  190.   char *startname, *parmname;
  191.  
  192.   struct MsgPort *mainmp;   /* pointer to main's msg port */
  193.   struct MsgPort *littleProc; /* pointer to spawned proc's msg port */
  194.  
  195.  /* Provide names for the messages we are passing so we can check the returned
  196.   * messages at the message ports.... that is if we choose to do so.
  197.   */
  198.   startname = "startermessage";
  199.   parmname  = "parameterpass";
  200.   
  201.  /* LOAD THE PROGRAM TO BE STARTED FROM MAIN ****************************** */
  202.  
  203.   littleSeg = LoadSeg("littleproc");
  204.   if(littleSeg == 0) 
  205.   {
  206.    printf("\nlittleproc not found");
  207.    exit(999);
  208.   }
  209.  
  210.  /* CREATE A PROCESS FOR THIS OTHER PROGRAM ******************************* */
  211.  
  212.   littleProc = CreateProc("littleguy",PRIORITY, littleSeg, STACKSIZE);
  213.   if( littleProc == 0 ) 
  214.   {
  215.    printf("\Couldn't create the process");
  216.    UnLoadSeg(littleSeg);
  217.    exit( 1000 );
  218.   }
  219.  
  220.  /* ********************************************************************** */
  221.  /* Locate the message port that is allocated as part of the process
  222.     that started main() in the first place */
  223.  
  224.   myprocess = (struct Process *)FindTask(0);
  225.  
  226.   mainmp = &myprocess->pr_MsgPort;
  227.  
  228.  /* ********************************************************************** */
  229.  /* THE FOLLOWING CODE BLOCK STARTS THE PROCESS RUNNING, 
  230.       AS THOUGH CALLED FROM WORKBENCH */
  231.  
  232.  /*   In fact, because we created the process the way that is shown
  233.   here, if you use the standard start-up code, the program must
  234.   be started as though called from Workbench.  It is now waiting for
  235.   a start-up message.   
  236.  
  237.   (There is, in fact, another way to call a loaded program's code,
  238.   but it does not entail starting another process.  Rather it
  239.   uses a direct call (as a subroutine) to the loaded code.  The
  240.   other program runs on your own stack, so your program must
  241.   have sufficient stack to handle both of you.  It also runs
  242.   under your own process, so your own program does not get
  243.   control until that other program has completed.  The program
  244.   return()'s or exit()'s to you, providing the appropriate
  245.   return code.  This run-loaded-subroutine topic is covered in 
  246.   a separate code sample.)
  247.  
  248.  ********************************************************************** */
  249.   
  250.   /* This message block is a wakeup call to the process we created. */
  251.   msg = (struct WBStartup *)AllocMem(sizeof(struct WBStartup), 
  252.    MEMF_CLEAR);
  253.   if(msg)
  254.   {
  255.    /* Preset the necessary arguments for message passing */
  256.  
  257.    msg->sm_Message.mn_ReplyPort = mainmp;
  258.    msg->sm_Message.mn_Length = sizeof(struct WBStartup);
  259.    msg->sm_Message.mn_Node.ln_Name = startname; 
  260.   
  261.    /* Passing no workbench arguments to this process; 
  262.     * we are not WBench.  Of course, if we want to pass 
  263.     * workbench-style arguments this way, we can.
  264.      */
  265.   
  266.    msg->sm_ArgList = NULL;
  267.   
  268.    /* If the process is being opened without a ToolWindow 
  269.     * (Workbench sets this up) as a parent, slave will simply
  270.     * go on to do its own main()  ... as shown in Astartup.asm
  271.     */
  272.   
  273.    msg->sm_ToolWindow = NULL;
  274.   
  275.    /* Send the start-up message */
  276.   
  277.    PutMsg(littleProc,msg); 
  278.   }
  279.   else
  280.   {
  281.    printf("\nCouldnt allocate mem for WBStartup!\n");
  282.    goto aarrgghh; /* Oh no, a "goto" ! */
  283.   } 
  284.   /* *************************************************************** */
  285.   /* Just a sample message, still using the same message and 
  286.    * reply ports
  287.    *
  288.    * Littleproc is a cooperating process...it KNOWS it must wait 
  289.    * until a message arrives at its port, containing the parameters 
  290.    * it should use for output.  
  291.    *
  292.    * The startup message is handled by the standard startup code.  
  293.    * This parameter message is handled by the program code itself.   
  294.    * The startup message is returned to the replyport by the startup 
  295.    * code, after the program code exits or returns.
  296.     */  
  297.  
  298.   parms = (struct MyMess *)AllocMem(sizeof(struct MyMess),MEMF_CLEAR);
  299.   if(parms)
  300.   {
  301.    parms->mm_Message.mn_ReplyPort = mainmp;
  302.    parms->mm_Message.mn_Length = sizeof(struct mymess);
  303.    parms->mm_Message.mn_Node.ln_Name = parmname;
  304.  
  305.    /* NOTE THAT THESE ARE THE AStartup.asm stdout and stderr; 
  306.     * the example works only if both master and slave are
  307.     * compiled and linked with the same startup code. */
  308.  
  309.    parms->mm_OutPointer = (int)stdout;
  310.    parms->mm_ErrPointer = (int)stderr;
  311.    /* send him our parameters */
  312.  
  313.    PutMsg(littleProc,parms); 
  314.   
  315.    /* wait for the reply from parameter pass. */
  316.  
  317.    WaitPort(mainmp); 
  318.   
  319.    reply = GetMsg(mainmp); 
  320.  
  321.    /* Message node name should contain the address of the 
  322.     * string "parms" if error checking was included.
  323.        *
  324.      * 
  325.     * User should probably allocate separate ports for 
  326.     * parameter passing different from the main port 
  327.     * automatically allocated by the system when a 
  328.     * process is initiated.  It would alleviate
  329.      * some of the checking that is appropriate to do 
  330.     * when multiple kinds of messages arrive at the same port.
  331.      *
  332.     * 
  333.     * NOW MAIN CAN GO ON AND DO SOMETHING USEFUL,
  334.     * LATER CAN COME BACK AND SEE IF SPAWNED PROCESS
  335.     * HAS COMPLETED AND IS READY TO BE UNLOADED.
  336.      *
  337.     * 
  338.     * Wait for the return of the wbstartup message before 
  339.     * main itself is allowed to exit.  
  340.     */
  341.  
  342.    WaitPort(mainmp); 
  343.  
  344.    reply = GetMsg(mainmp); 
  345.  
  346.    /* Message node name should be 
  347.     * address of "startermessage"  */
  348.  
  349.    /* NOTE: there should be checking here to see if the message
  350.      * received at this port was the string, or the wakeup call.
  351.      * This sample code only assumes that the string is received
  352.      * and replied first, then the wakeup call message is returned
  353.      * as the little task is exiting.  
  354.      */
  355.  
  356.    UnLoadSeg(littleSeg);
  357.  
  358.    printf("\nSlave exited; Master unloaded its code and data\n");
  359.   }
  360.   else
  361.   {
  362.    printf("\nCouldn't allocate memory for parameter message\n");
  363.   }
  364.      aarrgghh:
  365.    /* arrive here on good or bad exit */
  366.   
  367.   if(parms) { FreeMem( parms, sizeof(struct MyMess));    }
  368.   if(msg)   { FreeMem( msg,   sizeof(struct WBStartup)); }
  369.  
  370.  } /* end of main */
  371.  
  372.  
  373.  
  374.  
  375.  /* littleproc.c ******************************************************** */
  376.  
  377.  /* Sample slave code for create process test */
  378.  
  379.  /* author: Rob Peck   3/4/86  */
  380.  
  381.  /* system software version: V1.1               */
  382.  
  383.  #include "exec/types.h"
  384.  #include "exec/nodes.h"
  385.  #include "exec/lists.h"
  386.  #include "exec/libraries.h"
  387.  #include "exec/ports.h"
  388.  #include "exec/interrupts.h"
  389.  #include "exec/io.h"
  390.  
  391.  #include "libraries/dos.h"
  392.  #include "libraries/dosextens.h"
  393.  
  394.  #include "workbench/startup.h"
  395.  
  396.  /* these are going to be supplied to the slave by the starter */
  397.  /* they are actually defined in the startup code (Astartup.asm) */
  398.  
  399.  extern int stdout;
  400.  extern int stderr;
  401.  
  402.  struct MyMess {
  403.     struct Message mm_Message;
  404.     int mm_OutPointer;
  405.     int mm_ErrPointer;
  406.  };
  407.  
  408.  extern struct Message *GetMsg();
  409.  extern struct Task *FindTask();
  410.  extern struct FileHandle *Open();
  411.  
  412.  main()
  413.  { 
  414.   struct MyMess *msg;
  415.   struct MsgPort *myport;
  416.   struct Process *myprocess;
  417.  
  418.   struct FileHandle *myOwnOutput;
  419.  
  420.   myprocess = (struct Process *)FindTask(0);
  421.  
  422.   myport = &myprocess->pr_MsgPort;
  423.  
  424.   /* Wait for starter to post a message.  Special sample message 
  425.    * has his stderr, stdout so we can both post stuff to the 
  426.    * same CLI window as he started from */
  427.  
  428.   WaitPort(myport); 
  429.  
  430.   msg = (struct MyMess *)GetMsg(myport);
  431.  
  432.   
  433.   stdout = msg->mm_OutPointer;
  434.  
  435.   /* Use printf to prove that it is really a process... 
  436.    * a simple task cannot do this without crashing! */
  437.  
  438.   printf("\nHere I am, that slave process you started!!!");
  439.   printf("\nNow going to open MY OWN window.\n");
  440.  
  441.   /* NOW DO SOMETHING USEFUL... DO WHATEVER THE PROCESS WAS DESIGNED
  442.     * TO ACCOMPLISH.
  443.    */
  444.  
  445.   myOwnOutput = Open("CON:10/10/320/150/SlaveProcess",MODE_NEWFILE);
  446.   if(myOwnOutput == 0)
  447.   {
  448.    ReplyMsg(msg); /* tell main I'm done */
  449.    exit(0); /* can't return an error code anyhow */
  450.   }
  451.   else
  452.   {
  453.    stdout = (int)myOwnOutput; 
  454.    /* reset my output file handle */
  455.    printf("See, I can do AmigaDOS!");
  456.    Delay(250); /* 250/50 = 5 seconds */
  457.    stdout = msg->mm_OutPointer;
  458.    Close(myOwnOutput);
  459.    ReplyMsg(msg);
  460.   }
  461.  
  462.   /* Now simply fall off the end of the world, 
  463.    * returns to the startup code, and should exit cleanly */
  464.  
  465.  } 
  466.  
  467.  /* ******************************************************************* 
  468.  
  469.  As a final note, one could have created and started another process by 
  470.  using the Execute command of AmigaDOS:
  471.  
  472.   success = Execute("someprogram",0,0);
  473.  
  474.  but I went through this exercise to show interprocess communication
  475.  setup and message passing.  I hope that the sample code provides some
  476.  insight into the multi-processing capabilities of AmigaDOS.
  477.  
  478.  ******************************************************************* */
  479.